#include "StackAllocator.h"
namespace RE
{
	namespace MEM
	{
void StackAllocator::Initialize(U32 size)
{
	m_stack_start = (U8*)malloc(size);
	m_stack_size = size;
	m_stack_ptr = m_stack_start;
}
void StackAllocator::Destruct()
{
	free(m_stack_start);
	m_stack_size = 0;
	m_stack_ptr = 0;
	m_stack_start = 0;
}
void StackAllocator::PopFrame()
{
	m_stack_ptr -= *reinterpret_cast<U32*>(m_stack_ptr - 4);
	m_stack_ptr -= 4;
}
void StackAllocator::PopStack()
{
	m_stack_ptr = m_stack_start;
}
void* StackAllocator::PushFrame(U32 size)
{
	if(size + sizeof(U8*) <= m_stack_size - (m_stack_ptr - m_stack_start))
	{
		m_stack_ptr += size + sizeof(U8*);
		*reinterpret_cast<U32*>(m_stack_ptr - sizeof(U8*)) = size;
		return m_stack_ptr - size - sizeof(U8*);
	}else
	{
		assert(0 && "No free memory in the stack!");
		return NULL;
	}
}



void SingleFrameAllocator::OnUpdate()
{
	StackAllocator::PopStack();
}

void SingleFrameAllocator::PopFrame()
{
	//do nothing
}


void DoubleBufferedAllocator::Initialize(U32 size)
{
	m_stacks[0].Initialize(size);
	m_stacks[1].Initialize(size);
	m_active = 0;
}
void DoubleBufferedAllocator::Destruct()
{
	m_stacks[0].Destruct();
	m_stacks[1].Destruct();
}
void DoubleBufferedAllocator::PopFrame()
{
	//do nothing
}
void DoubleBufferedAllocator::PopStack()
{
	//do nothing
}
void DoubleBufferedAllocator::OnUpdate()
{
	m_active = !m_active;
	m_stacks[m_active].OnUpdate();
}
void* DoubleBufferedAllocator::PushFrame(U32 size)
{
	return m_stacks[m_active].PushFrame(size);
}


void DoubleEndedAllocator::Initialize(U32 size)
{
	m_stack_front = (U8*)malloc(size);
	m_stack_back = m_stack_front + size;
	m_stack_size = size;
	m_stack_ptrf = m_stack_front;
	m_stack_ptrb = m_stack_back;
}
void DoubleEndedAllocator::Destruct()
{
	free(m_stack_front);
	m_stack_front = 0;
	m_stack_back = 0;
}

void DoubleEndedAllocator::PopFrame()
{
	if(m_stack_ptrf - m_stack_front < m_stack_back - m_stack_ptrb)
	{
		PopFrameFront();
	}else
	{
		PopFrameFront();
	}
}
void DoubleEndedAllocator::PopFrameBack()
{
	if(m_stack_back == m_stack_ptrb)
	{
		return;
	}
	m_stack_ptrb += *reinterpret_cast<U32*>(m_stack_ptrb + 1);
	m_stack_ptrb += sizeof(U32);
}
void DoubleEndedAllocator::PopFrameFront()
{
	if(m_stack_front == m_stack_ptrf)
	{
		return;
	}
	m_stack_ptrf -= *reinterpret_cast<U32*>(m_stack_ptrf - sizeof(U32));
	m_stack_ptrf -= sizeof(U32);
}

void DoubleEndedAllocator::PopStack()
{
	m_stack_ptrf = m_stack_front;
	m_stack_ptrb = m_stack_back;
}
void DoubleEndedAllocator::PopStackBack()
{
	m_stack_ptrb = m_stack_back;
}
void DoubleEndedAllocator::PopStackFront()
{
	m_stack_ptrf = m_stack_front;
}

void DoubleEndedAllocator::OnUpdate()
{
	//do nothing
}
void* DoubleEndedAllocator::PushFrame(U32 size)
{
	if(m_stack_ptrf - m_stack_front < m_stack_back - m_stack_ptrb)
	{
		return PushFrameFront(size);
	}else
	{
		return PushFrameBack(size);
	}
}
void* DoubleEndedAllocator::PushFrameBack(U32 size)
{
	if(m_stack_size - (m_stack_ptrf - m_stack_front) - (m_stack_back - m_stack_ptrb) >= size + sizeof(U32))
	{
		m_stack_ptrb -= size + sizeof(U32);
		*reinterpret_cast<U32*>(m_stack_ptrb + 1) = size;
		return m_stack_ptrb + sizeof(U32) + 1;
	}
	else
	{
		assert(0 && "No enough memory!");
		return NULL;
	}
}
void* DoubleEndedAllocator::PushFrameFront(U32 size)
{
	if(m_stack_size - (m_stack_ptrf - m_stack_front) - (m_stack_back - m_stack_ptrb) >= size + sizeof(U32))
	{
		m_stack_ptrf += size + sizeof(U32);
		*reinterpret_cast<U32*>(m_stack_ptrf - sizeof(U32)) = size;
		return m_stack_ptrf - sizeof(U32) - size;
	}
	else
	{
		assert(0 && "No enough memory!");
		return NULL;
	}
}

	} // end of namespace MEMORY
} // end of namespace Random Engine